// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::structure::*;
use core::cell::RefCell;
use std::{collections::BTreeMap, rc::Rc};

/// `NodeMap` structure, which corresponds to the `Interface NodeMap`.
/// Users generally do not create `NodeMap` directly, but obtain it from the
/// return value of some methods.
///
/// # Association
/// [Interface NamedNodeMap](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1780488922)
///
/// # Example
/// ```
/// use ylong_xml::{Document, DocumentMethod, ElementMethod};
///
/// let document = Document::new();
/// let element = document.create_element("Element").unwrap();
/// let attributes = element.attributes();
/// assert_eq!(attributes.len(), 0);
/// ```
#[derive(Debug, Clone)]
pub struct NodeMap<T> {
    pub(crate) map: Rc<RefCell<BTreeMap<String, T>>>,
}

impl<T: NodeMethod + Clone> NodeMap<T> {
    /// Get the number of nodes in the map.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ElementMethod};
    ///
    /// let document = Document::new();
    /// let element = document.create_element("Element").unwrap();
    /// let attributes = element.attributes();
    /// assert_eq!(attributes.len(), 0);
    /// ```
    pub fn len(&self) -> usize {
        self.map.borrow().len()
    }

    /// Check if a `NodeMap` is empty.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ElementMethod};
    ///
    /// let document = Document::new();
    /// let element = document.create_element("Element").unwrap();
    /// let attributes = element.attributes();
    /// assert_eq!(attributes.is_empty(), true);
    /// ```
    pub fn is_empty(&self) -> bool {
        self.map.borrow().is_empty()
    }

    /// Retrieve a node specified by name.
    ///
    /// # Association
    /// [NamedNodeMap](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1074577549)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ElementMethod};
    ///
    /// let document = Document::new();
    /// let element = document.create_element("Element").unwrap();
    /// let attributes = element.attributes();
    /// assert!(attributes.get("Attribute").is_none());
    /// ```
    pub fn get(&self, name: &str) -> Option<T> {
        self.map.borrow().get(name).cloned()
    }

    /// Set a new node.
    pub(crate) fn set(&mut self, node: T) -> Option<T> {
        self.map.borrow_mut().insert(node.name(), node)
    }

    /// Remove a node with the given name.
    pub(crate) fn remove(&mut self, name: &str) -> Option<T> {
        self.map.borrow_mut().remove(name)
    }

    /// Check if this `NodeMap` contains a node with the given name.
    pub(crate) fn contains(&self, name: &str) -> bool {
        self.map.borrow().contains_key(name)
    }
}

impl<T> Default for NodeMap<T> {
    fn default() -> Self {
        Self {
            map: Rc::new(RefCell::new(BTreeMap::new())),
        }
    }
}
